import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Map;

/**
 * @author fkjslee
 * @date 2018/2/11 at 14:22
 * Copyright (c) 2018 fkjslee. All rights reserved.
 */

public class Reflect {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        BaseClass baseClass = new BaseClass();

        System.out.println("属性");
        //属性
        Field[] fields = baseClass.getClass().getDeclaredFields();
        for(Field field : fields) {
            System.out.println("get attr name:\t" + getAttrName(field));
            System.out.println("get attr value:\t" + getAttrValue(field, baseClass));
            System.out.println("set attr value:\t" + setAttrValue(field, 0, baseClass));
            System.out.println("after set attr value:\t" + getAttrValue(field, baseClass));
        }

        System.out.println("\n函数");
        //函数
        for(Method method : baseClass.getClass().getDeclaredMethods()) {
            System.out.println("get method name :\t" + getMethodName(method));
            System.out.println("use method :\t" + useMethod(method, baseClass));
            System.out.println("after use method\t: " + method.getName() + " param3 = " + getAttrValue(baseClass.getClass().getDeclaredField("param3"), baseClass));
        }

        System.out.println("\n注解:");
        //注解
        for(Field field : baseClass.getClass().getDeclaredFields()) {
            System.out.println("field name :\t" + getAttrName(field));
            for(Annotation annotation : field.getAnnotations()) {
                System.out.println(annotation.annotationType());
                Map annotationMap = getAnnotationMap(annotation);
                for(Object temp : annotationMap.keySet()) {
                    String key = temp.toString();
                    System.out.println("key = \t" + key + " value = " + String.valueOf(annotationMap.get(key)));
                }
            }
        }
    }

    private static Map getAnnotationMap(Annotation annotation) {
        Map ret = null;
        try {
            InvocationHandler handler = Proxy.getInvocationHandler(annotation);
            Field memberValues = handler.getClass().getDeclaredField("memberValues");
            memberValues.setAccessible(true);
            ret = (Map) memberValues.get(handler);
        } catch(Exception e) {
            e.printStackTrace();
        }
        return ret;
    }

    private static String useMethod(Method method, BaseClass baseClass) {
        method.setAccessible(true);
        switch(method.getName()) {
            case "getParam3":
                try {
                    return "getParam3 : " + method.invoke(baseClass);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            case "setParam3":
                try {
                    method.invoke(baseClass, 10);
                    return "setParam 3 done";
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
        return "error";
    }

    private static String getMethodName(Method method) {
        return method.getName();
    }

    private static String setAttrValue(Field field, int i, BaseClass baseClass) {
        field.setAccessible(true);
        try {
            field.setAccessible(true);
            Field modifier = Field.class.getDeclaredField("modifiers");
            modifier.setAccessible(true);
            modifier.setInt(field, field.getModifiers() & ~(Modifier.FINAL | Modifier.STATIC));
            field.set(baseClass, i);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "done";
    }

    private static String getAttrValue(Field field, BaseClass baseClass) {
        String ret = "";
        try {
            field.setAccessible(true);
            Field modifier = Field.class.getDeclaredField("modifiers");
            modifier.setAccessible(true);
            modifier.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            ret = field.get(baseClass).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ret;
    }

    private static String getAttrName(Field field) {
        return field.getName();
    }

}
